# -*- coding: utf-8 -*-

import httplib
import socket

from time import time

class _AEConnection:
	""" This class is the main connection to the google
		app engine server. No originDB specific implementation
		is here, it should all be in the other files in this module.
		Using this object you should only make method calls and the
		methods with _ prepending them are private.
		Here is where we keep track of the session
	"""

	def __init__(self, host=""):
		""" Host is the name of the host we are connecting to.
			The actual connection is created in this method
		"""
		self.host = host
		self._conn = None
		self._sID = ''
		self._errorCallback = None
		self._startCallback = None
		self._endCallback = None
		self._updateCallback = None

		self._lastTimeAccessed = 0
		self._inAction = 0
		
		#	if we were provided with a host
		if host:
			#	connect using our class function
			self.connect(host)


	def connect(self, host=""):
		""" Connect to the host provided or reconnect to old host
		"""
		self._startAction()
		
		if self._conn:
			self._conn.close()

		if not self._conn:
			
			if not host:
				if self.host:
					host = self.host
				else:
					self._endAction()
					return

			self._conn = httplib.HTTPConnection(host)
		
		try:
			self._conn.connect()
		except socket.error as err_val:
			err_msg = 'Error ' + str(err_val.errno) + ': ' + err_val.strerror

			if self._endCallback:
				self._endCallback()

			self._inAction = 0
			self._lastTimeAccessed = 0
			
			if self._errorCallback:
				self._errorCallback(err_msg)
			raise DatabaseError(err_msg)

		self._endAction()


	def setSID(self, sID):
		self._sID = sID


	def userLoggedIn(self):
		if self._sID:
			return True
		else:
			return False


	def _makeURL(self, action, params):
		""" Make a url that our GAE server can understand
		"""
		params['sID'] = self._sID
		
		url = "/" + action + "?"
		for key in params:
			url += str(key)+'='+str(params[key])+'&'

		return url


	def _startAction(self):
		
		if not self._inAction and self._startCallback:
			self._startCallback()
			
		self._inAction += 1
		
		# if it has been more than 60 seconds since we have made a
		# connection, reconnect.
		if time() - self._lastTimeAccessed > 60 and not self._inAction > 1:
			self.connect()


	def _endAction(self):
		
		self._inAction -= 1

		self._lastTimeAccessed = time()

		if not self._inAction and self._endCallback:
			self._endCallback()


	def getAction(self, action, params):
		""" -action is the action you want to perform on the server
			(i.e. get, login, search)
			-params are the params that action requires.
			-return value is the body of the response from the server
			this is normally some yaml.
		"""
		self._startAction()
		
		self._conn.request("GET", self._makeURL(action, params))

		try:
			resp = self._conn.getresponse()
		except httplib.BadStatusLine:
			if self._endCallback:
				self._endCallback()
				
			if self._errorCallback:
				self._errorCallback("Bad status line")
			self.connect()
			return

		if resp.status != httplib.OK:
			resp.read()
			
			if self._endCallback:
				self._endCallback()
				
			if self._errorCallback:
				self._errorCallback(resp.reason)
			raise DatabaseError(resp.reason)

		body = resp.read()

		self._endAction()

		return body


	def postAction(self, action, body, params):
		""" -action is the action you want to perform on the server
			(i.e. get, login, search)
			-params are the params that action requires.
			-return value is the body of the response from the server
			this is normally some yaml.
		"""
		self._startAction()
		
		self._conn.request("POST", self._makeURL(action, params), body)

		try:
			resp = self._conn.getresponse()
		except httplib.BadStatusLine:
			if self._endCallback:
				self._endCallback()
			
			if self._errorCallback:
				self._errorCallback("Bad status line")
			self.connect()
			return

		if resp.status != httplib.OK:
			resp.read()

			if self._endCallback:
				self._endCallback()

			if self._errorCallback:
				self._errorCallback(resp.reason)
			raise DatabaseError(resp.reason)

		body = resp.read()

		self._endAction()

		return body
		

	def setErrorHandler(self, func):
		self._errorCallback = func
		
	def setDBStartHandler(self, func):
		self._startCallback = func
		
	def setDBEndHandler(self, func):
		self._endCallback = func

	def setDBUpdateHandler(self, func):
		self._updateCallback = func

class DatabaseError(Exception):

	def __init__(self, value):
		self.value = value
		print value

	def __str__(self):
		return str(value)

dbHandle = _AEConnection()
